package server

import (
	"time"

	"github.com/kumahq/kuma/pkg/core"
	util_xds "github.com/kumahq/kuma/pkg/util/xds"
)

var nackLog = core.Log.WithName("kds-delta").WithName("nack-backoff")

type nackBackoff struct {
	backoff time.Duration
	util_xds.NoopCallbacks
}

var _ util_xds.DeltaCallbacks = &nackBackoff{}

func NewNackBackoff(backoff time.Duration) util_xds.DeltaCallbacks {
	return &nackBackoff{
		backoff: backoff,
	}
}

func (n *nackBackoff) OnStreamDeltaResponse(_ int64, request util_xds.DeltaDiscoveryRequest, _ util_xds.DeltaDiscoveryResponse) {
	if request.HasErrors() {
		// When DiscoveryRequest contains errors, it means that a control plane rejected configuration generated by the other control plane
		// It may happen for several reasons:
		// 1) Eventual consistency - ex. MeshTrafficPermission, but Mesh for this TrafficPermission is not synced yet.
		// 2) Config is valid from one control plane side but invalid from the other side - ex. schema is broken
		//
		// Second case is especially dangerous because we will end up in a loop.
		// CP is constantly trying to send a config and other cp immediately rejects the config.
		// Without this backoff, CP is under a lot of pressure from faulty control plane.
		//
		// It is safe to sleep here because OnStreamResponse is executed in the goroutine of a single ADS stream
		nackLog.Info("config was previously rejected by other control plane. Applying backoff before resending it", "backoff", n.backoff, "nodeID", request.NodeId(), "reason", request.ErrorMsg())
		time.Sleep(n.backoff)
	}
}
